Target Info ~"~"~"~"~"~ Name : Opera v3.50 URL : http://www.operasoftware.com Protection : Serial, 30-day limit trial. Introduction ~"~"~"~"~"~" Hi there! In this tutorial, I'm going to try my best to explain how I got the actual serial for Opera v3.50. What?! You never heard of this program before?? What a shame! Go check their site and get this simply amazing browser (there, I've given a hint on what this proggie is about) ;) The process of getting the serial for this baby is long, very very long. As we all know, name/serial kind of protection schemes are very very easy, so programmers try to make these kind of schemes to be very very complicated, or try to bore us to death! Believe it or not, the programmer almost succeeded in doing so ;) UCF have already keygened it, so I thought that maybe I should try to get the serial myself, and so I did. OK, I'll try my very very best to explain how this baby works. This tutorial is not going to be newbie-orientated, so if there's anything you don't understand in this tutorial, go learn to crack something else first or learn more asm. OK, time to cut the crap and get on with the serial fishing ;) Tools Needed ~"~"~"~"~"~" Softice v3.24 IDA Pro v3.8a The Essay ~"~"~"~"~ I'll jump straight into the serial calculation part. I won't be telling you how to do 'bpx getdlgitemtexta' or 'bpr xxxx:xxxxxxxxx xxxx:xxxxxxxx rw' or other softice commands. This is going to be a softice+IDA approach. I'll be assuming that you already set up softice, know how to use it and the commands, know how to set up IDA and familiar with its capabilities. As usual, do the normal stuff of bpx getdlgitemtexta, bpr name/company/serial, F5, analyze code, trace through them, new bprs if name/company/serial is being copied to a new place, guessing what they are doing by looking at the changes done towards the code, comparing the codes with the disassembly in IDA, etc. etc. There are lots of things going on to your serial, mostly dummy modification or whatever you call it. Eventually, you should reach this part of code. Compare the disassembly with what you see in IDA. :0048595D 53 push ebx :0048595E 57 push edi :0048595F 56 push esi :00485960 E8FB6F0500 call 004DC960 <-- _memcpy :00485965 832600 and dword ptr [esi], 00000000 :00485968 8D9E38010000 lea ebx, dword ptr [esi+00000138] :0048596E 83C40C add esp, 0000000C :00485971 85DB test ebx, ebx :00485973 0F84C5000000 je 00485A3E :00485979 803B00 cmp byte ptr [ebx], 00 :0048597C 0F84BC000000 je 00485A3E :00485982 8D8690030000 lea eax, dword ptr [esi+00000390] :00485988 85C0 test eax, eax :0048598A 0F84AE000000 je 00485A3E :00485990 803800 cmp byte ptr [eax], 00 :00485993 0F84A5000000 je 00485A3E :00485999 53 push ebx :0048599A E8E76E0200 call 004AC886 :0048599F 8D8664020000 lea eax, dword ptr [esi+00000264] :004859A5 50 push eax :004859A6 E8DB6E0200 call 004AC886 :004859AB 8D8690030000 lea eax, dword ptr [esi+00000390] * Possible StringData Ref from Data Obj ->" -,." | :004859B1 6834C64F00 push 004FC634 :004859B6 50 push eax :004859B7 E8A1F5F9FF call 00424F5D :004859BC 83C410 add esp, 00000010 :004859BF 803B00 cmp byte ptr [ebx], 00 :004859C2 747A je 00485A3E :004859C4 80BE9003000000 cmp byte ptr [esi+00000390], 00 :004859CB 7471 je 00485A3E :004859CD 8D9E90030000 lea ebx, dword ptr [esi+00000390] :004859D3 53 push ebx :004859D4 E8B7690500 call 004DC390 <-- _strlen :004859D9 83F80C cmp eax, 0000000C <-- cmp length of serial with 12 chars :004859DC 59 pop ecx :004859DD 751E jne 004859FD <-- jmp if not equal 12 :004859DF 57 push edi :004859E0 E85C690500 call 004DC341 <-- ??3@YAXPAX@Z * Possible Reference To String Resource ID=22005: "Invalid number. You have..." | :004859E5 C70424F5550000 mov dword ptr [esp], 000055F5 :004859EC 6A11 push 00000011 :004859EE 68A7900000 push 000090A7 There, quite a lot of code here. But don't worry, you don't have to know all of this. The important one is at :004859D4 call _strlen. Simple deduction will tell you that your serial should not be 12 characters long. Trace further (make sure your dummy serial is not 12 characters long), and you will come to this part. :004859FD 6A0C push 0000000C :004859FF 8D4594 lea eax, dword ptr [ebp-6C] :00485A02 53 push ebx :00485A03 50 push eax :00485A04 E8E7790500 call 004DD3F0 <-- _strncpy :00485A09 8065A000 and byte ptr [ebp-60], 00 :00485A0D 8D4594 lea eax, dword ptr [ebp-6C] :00485A10 50 push eax :00485A11 8D45D4 lea eax, dword ptr [ebp-2C] :00485A14 50 push eax :00485A15 E8866B0500 call 004DC5A0 <-- unknown_libname_9 :00485A1A 8D45D4 lea eax, dword ptr [ebp-2C] :00485A1D 50 push eax :00485A1E E8BA860000 call 0048E0DD :00485A23 8D45D4 lea eax, dword ptr [ebp-2C] :00485A26 50 push eax :00485A27 8D4594 lea eax, dword ptr [ebp-6C] :00485A2A 50 push eax :00485A2B E8E0690500 call 004DC410 <-- _strcmp :00485A30 83C420 add esp, 00000020 :00485A33 85C0 test eax, eax :00485A35 7507 jne 00485A3E :00485A37 C7450C01000000 mov [ebp+0C], 00000001 Using softice and tracing through this part of code, you will see that your dummy serial is being modified at line 485A1E. My dummy was 1234567890, and it was changed to 1234B3KY2pb4. We know that our serial must not be 12 characters long. This means the generated serial is not full. Also notice that the first four digits of our serial is left untouched. This might mean that our serial is calculated from the first four digits of our serial, and our name/company has nothing to do with it. Now, time for some light thinking. The generated serial is 12 characters long, and our serial should not be 12 characters long. So how are we going to 'pass' the _strcmp? Easy, use the generated serial as a dummy, but add more characters. In my case, my new dummy serial is 1234B3KY2pb41234567890. Tracing further with the new dummy serial, you will eventually reach this part of code. :0048DDBC E8CFE50400 call 004DC390 <-- _strlen :0048DDC1 83F840 cmp eax, 00000040 :0048DDC4 59 pop ecx :0048DDC5 0F83D7000000 jnb 0048DEA2 :0048DDCB 8D45C0 lea eax, dword ptr [ebp-40] :0048DDCE 56 push esi :0048DDCF 50 push eax :0048DDD0 E8CBE70400 call 004DC5A0 <-- unknown_libname_9 :0048DDD5 8D45C0 lea eax, dword ptr [ebp-40] :0048DDD8 68605D4F00 push 004F5D60 :0048DDDD 50 push eax :0048DDDE E87A71F9FF call 00424F5D :0048DDE3 8D45C0 lea eax, dword ptr [ebp-40] :0048DDE6 50 push eax :0048DDE7 E8A4E50400 call 004DC390 <-- _strlen :0048DDEC 83C414 add esp, 00000014 :0048DDEF 83F80C cmp eax, 0000000C :0048DDF2 0F8EAA000000 jle 0048DEA2 :0048DDF8 8D45C0 lea eax, dword ptr [ebp-40] :0048DDFB 6A0C push 0000000C :0048DDFD 50 push eax :0048DDFE 8D8560FFFFFF lea eax, dword ptr [ebp+FFFFFF60] :0048DE04 50 push eax :0048DE05 E8E6F50400 call 004DD3F0 <-- _strncpy :0048DE0A 8D45CC lea eax, dword ptr [ebp-34] :0048DE0D 889D6CFFFFFF mov byte ptr [ebp+FFFFFF6C], bl :0048DE13 50 push eax :0048DE14 8D8520FFFFFF lea eax, dword ptr [ebp+FFFFFF20] :0048DE1A 50 push eax :0048DE1B E880E70400 call 004DC5A0 <-- unknown_libname_9 :0048DE20 8D8560FFFFFF lea eax, dword ptr [ebp+FFFFFF60] :0048DE26 50 push eax :0048DE27 8D45A0 lea eax, dword ptr [ebp-60] :0048DE2A 50 push eax :0048DE2B E870E70400 call 004DC5A0 <-- unknown_libname_9 :0048DE30 8D45A0 lea eax, dword ptr [ebp-60] :0048DE33 50 push eax :0048DE34 E8A4020000 call 0048E0DD :0048DE39 8D45A0 lea eax, dword ptr [ebp-60] :0048DE3C 50 push eax :0048DE3D 8D8560FFFFFF lea eax, dword ptr [ebp+FFFFFF60] :0048DE43 50 push eax :0048DE44 E8C7E50400 call 004DC410 <-- _strcmp :0048DE49 83C428 add esp, 00000028 :0048DE4C 85C0 test eax, eax :0048DE4E 7552 jne 0048DEA2 :0048DE50 8D8520FFFFFF lea eax, dword ptr [ebp+FFFFFF20] :0048DE56 50 push eax :0048DE57 E834E50400 call 004DC390 :0048DE5C 33F6 xor esi, esi :0048DE5E 3BC3 cmp eax, ebx :0048DE60 59 pop ecx :0048DE61 7E24 jle 0048DE87 :0048DE63 0FBE943520FFFFFF movsx edx, byte ptr [ebp+esi-000000E0] :0048DE6B 8D8C3520FFFFFF lea ecx, dword ptr [ebp+esi-000000E0] :0048DE72 83EA61 sub edx, 00000061 :0048DE75 7408 je 0048DE7F :0048DE77 4A dec edx :0048DE78 7508 jne 0048DE82 :0048DE7A C60131 mov byte ptr [ecx], 31 :0048DE7D EB03 jmp 0048DE82 :0048DE7F C60130 mov byte ptr [ecx], 30 :0048DE82 46 inc esi :0048DE83 3BF0 cmp esi, eax :0048DE85 7CDC jl 0048DE63 :0048DE87 8D8520FFFFFF lea eax, dword ptr [ebp+FFFFFF20] :0048DE8D 50 push eax :0048DE8E E8FDE70400 call 004DC690 <-- _atol :0048DE93 59 pop ecx :0048DE94 33D2 xor edx, edx :0048DE96 6A07 push 00000007 :0048DE98 59 pop ecx :0048DE99 F7F1 div ecx :0048DE9B 85D2 test edx, edx :0048DE9D 7503 jne 0048DEA2 :0048DE9F 6A01 push 00000001 :0048DEA1 5B pop ebx :0048DEA2 5F pop edi :0048DEA3 8BC3 mov eax, ebx :0048DEA5 5E pop esi :0048DEA6 5B pop ebx :0048DEA7 C9 leave :0048DEA8 C3 ret Whoa!!! Lots of code there! But don't worry, with the comments you get with IDA (yep, IDA wins again ;), you will easily understand this piece very easily. Tracing this part with softice, you will see that it does the same thing as the previous code (_strlen, unknown_libname_9, _strncpy, etc. etc.). Now, here's something interesting. At line 48DE8E call _atol. Now, what the heck is this command doing? Well, check with the help files which came together with Borland C++. I don't have mine with me now, so I can't actually tell you the actual purpose. It takes my last 10 digits (1234567890) and turn it into hex in eax. Then it is divided by 7 at line 48DE99. The result is stored at eax, and the remainder in edx. It then tests edx to see if it is 0. It will jump if it isn't 0. Now, if you leave all your bprs active, you will jump to the 'bad serial' in no time. Now, lets try changing the zero flag at that jne line. So, instead of the nag, you will end up here. (Remember that when you restart the tracing process, make sure you break here and alter the zero flag to bring you to the next stage of the calculation process) :0048DEA9 55 push ebp :0048DEAA 8BEC mov ebp, esp :0048DEAC 81EC80000000 sub esp, 00000080 :0048DEB2 56 push esi :0048DEB3 8B7508 mov esi, dword ptr [ebp+08] :0048DEB6 57 push edi :0048DEB7 56 push esi :0048DEB8 33FF xor edi, edi :0048DEBA E848000000 call 0048DF07 :0048DEBF 59 pop ecx :0048DEC0 8B4D0C mov ecx, dword ptr [ebp+0C] :0048DEC3 85C0 test eax, eax :0048DEC5 8901 mov dword ptr [ecx], eax :0048DEC7 7538 jne 0048DF01 :0048DEC9 8D460C lea eax, dword ptr [esi+0C] :0048DECC 50 push eax :0048DECD 8D4580 lea eax, dword ptr [ebp-80] :0048DED0 50 push eax :0048DED1 E8CAE60400 call 004DC5A0 <-- unknown_libname_9 :0048DED6 8D45C0 lea eax, dword ptr [ebp-40] :0048DED9 56 push esi :0048DEDA 50 push eax :0048DEDB E8C0E60400 call 004DC5A0 <-- unknown_libname_9 :0048DEE0 8065CC00 and byte ptr [ebp-34], 00 :0048DEE4 8D45C0 lea eax, dword ptr [ebp-40] :0048DEE7 50 push eax :0048DEE8 E898000000 call 0048DF85 :0048DEED 8D45C0 lea eax, dword ptr [ebp-40] :0048DEF0 56 push esi :0048DEF1 50 push eax :0048DEF2 E819E50400 call 004DC410 <-- _strcmp :0048DEF7 83C41C add esp, 0000001C :0048DEFA 85C0 test eax, eax :0048DEFC 7503 jne 0048DF01 :0048DEFE 6A01 push 00000001 :0048DF00 5F pop edi :0048DF01 8BC7 mov eax, edi :0048DF03 5F pop edi :0048DF04 5E pop esi :0048DF05 C9 leave :0048DF06 C3 ret Oh boy...more code. Hehe, getting sleepy already?? Go wash yourself up and come back if you are. Tracing with softice here, you will see that at line 48DEC7, you will jump right to the end of this procedure. Looking up a few lines, you see a very strange call. Looking inside that call in IDA, here's what you should find. .text:0048DF07 55 push ebp .text:0048DF08 8B EC mov ebp, esp .text:0048DF0A 83 EC 40 sub esp, 40h .text:0048DF0D 53 push ebx .text:0048DF0E 8B 5D 08 mov ebx, [ebp+arg_0] .text:0048DF11 56 push esi .text:0048DF12 57 push edi .text:0048DF13 0F B6 03 movzx eax, byte ptr [ebx] .text:0048DF16 50 push eax .text:0048DF17 E8 1E EF 04 00 call _isupper <-- funny eh? .text:0048DF1C 85 C0 test eax, eax .text:0048DF1E 59 pop ecx .text:0048DF1F 74 5F jz short _text_48DF80 <-- jump if not uppercase .text:0048DF21 BF EC C9 4F 00 mov edi, offset _data_4FC9EC .text:0048DF26 FF 37 push dword ptr [edi] .text:0048DF28 8D 45 C0 lea eax, [ebp+var_40] .text:0048DF2B 50 push eax .text:0048DF2C E8 6F E6 04 00 call unknown_libname_9 .text:0048DF31 8D 45 C0 lea eax, [ebp+var_40] .text:0048DF34 68 B4 F0 4E 00 push offset str->_-_ .text:0048DF39 50 push eax .text:0048DF3A E8 1E 70 F9 FF call _text_424F5D .text:0048DF3F 8D 45 C0 lea eax, [ebp+var_40] .text:0048DF42 50 push eax .text:0048DF43 E8 48 E4 04 00 call _strlen .text:0048DF48 50 push eax .text:0048DF49 8D 45 C0 lea eax, [ebp+var_40] .text:0048DF4C 50 push eax .text:0048DF4D 53 push ebx .text:0048DF4E E8 FD E3 04 00 call _strncmp .text:0048DF53 8B F0 mov esi, eax .text:0048DF55 6A 00 push 0 .text:0048DF57 F7 DE neg esi .text:0048DF59 8D 45 C0 lea eax, [ebp+var_40] .text:0048DF5C 6A 40 push 40h .text:0048DF5E 1B F6 sbb esi, esi .text:0048DF60 50 push eax .text:0048DF61 46 inc esi .text:0048DF62 E8 39 ED 04 00 call _memset .text:0048DF67 83 C4 2C add esp, 2Ch .text:0048DF6A 85 F6 test esi, esi .text:0048DF6C 75 12 jnz short _text_48DF80 .text:0048DF6E 83 C7 04 add edi, 4 .text:0048DF71 81 FF 30 CA 4F 00 cmp edi, offset str->UJ .text:0048DF77 7C AD jl short _text_48DF26 .text:0048DF79 33 C0 xor eax, eax .text:0048DF7B 5F pop edi .text:0048DF7C 5E pop esi Hmm...isn't that call _isupper funny? It is seeing if our serial has an uppercase. Checking with softice, you will find that it is checking our FIRST digit. Now, our dummy serial starts with 1, not an uppercase letter. So, it will jump to the end of our procedure. Now, with this new knowledge, lets alter our dummy serial a little bit and change the first character to A or any other uppercase letter you like. Remember that the serial is being calculated from our first 4 characters. By changing the first character to A, your rest of the code will also change. So, you will have to go over the whole process again (lets hope you are still keeping your breakpoints ;) a good cracker NEVER clears their breakpoints unless necessary or they feel absolutely sure that they don't need it anymore). I used A234 as my first four digits, so my first half of the serial would be A234Ko2TiTpB. Therefore, my dummy serial is now A234Ko2TiTpB1234567890. Check with softice to make sure that you don't jump after the _isupper call. After the _isupper check, you will see a lot of calls and compares with your dummy serial. TAKE NOTE THAT ALL THESE COMPARES ISN'T TRUE. Get out of this procedure and you will end up again at the second last piece of code in this tutorial. By memory dumping the registers just before and after the line 48DEE8, you will see that your dummy is being compared with A234Ko2TiTpBaa59279675. Ok, lets try registering with this serial (with all your breakpoints disabled of course ;) ). Did it work?? YES!! You have registered your copy of Opera 3.50! Now, as an excercise, try making a keygen for it. Final Notes ~"~"~"~"~"~ If you think that this tutorial is too hard to understand, read Neural_Noise' tutorial instead. That simply AMAZING tutorial explains well how to strip Opera's protection scheme naked and find the place to patch. GOOD WORK NEURAL! I admit that I really love your tutorial too. Well, that's all for now. ytc_, signing off... Group greets : Phrozen Crew, MiB, MASSiVE, DEViOUS, ECG, PCG, HERITAGE, tNO Personal greets : Iczelion, Kwai_Lo, HEAT (do not give up, ok? ;) ), JosephCo, Flu[X], Fresh--, blorght, immoral, Sleepers, Pr0phecy, Neural_N, KingGatso, ufk, _masta_, C4ffeine, Icecream, WKT_White, Sixx, +Malattia, +Cruehead, Icedragon, The+Q, HarvestR, BuLLeT, +gthorne (long time no see ;)), Ghiribizzo, and not to forget to those who shed light on me about cracking/reverse engineering (+ORC, Fravia+, Sandman, Mammon) (If I miss anyone out or you don't like the order of my greets, please forgive me, I'm having a fever now as I'm finishing this tutorial) Good luck! ytc_